#ifndef _FUNCTION_H_
#define _FUNCTION_H_

#include <utility>
#include <math.h>
#include <iostream>
#include <vector>
#include <map>
#if defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__CYGWIN32__)
#include <corecrt_math_defines.h>
#endif
#include "Mode.h"

typedef bool (*funType)(const double *inputs, double *const outputs);

class Function
{ // 一维和二维
private:
    funType m_f, m_df, m_laplace;

public:
    Function(funType f, funType df, funType laplace) : m_f(f), m_df(df), m_laplace(laplace) {}

    template <class... Ts>
    inline double operator()(Ts... args)
    {
        double output;
        const int size = sizeof...(args);
        double inputs[size] = {((double)args)...};
        m_f(inputs, &output);
        return (double)output;
    }

    template <class... Ts>
    inline double *dev(Ts... args)
    {
        const int inputsSize = sizeof...(args);
        double inputs[inputsSize] = {(double)args...};
        double *outputs = new double[inputsSize];
        memset(outputs, 0, sizeof(double) * inputsSize);
        m_df(inputs, outputs);
        return outputs;
    }

    template <class... Ts>
    inline double dev1d(Ts... args)
    {
        double *res = dev(args...);
        double output = res[0];
        delete[] res;
        return output;
    }

    template <class... Ts>
    inline double laplace(Ts... args)
    {
        double output;
        const int size = sizeof...(args);
        double inputs[size] = {(double)args...};
        m_laplace(inputs, &output);
        return (double)output;
    }
};

namespace DIM_NAMESPACE(One)
{
    namespace Default
    {
        inline bool f(const double *inputs, double *const outputs)
        {
            auto &x = inputs[0];
            auto &fx = outputs[0];
            fx = exp(sin(M_PI * x));
            return true;
        }

        inline bool df(const double *inputs, double *const outputs)
        {
            auto &x = inputs[0];
            auto &dfx = outputs[0];
            dfx = M_PI * cos(M_PI * x) * exp(sin(M_PI * x));
            return true;
        }

        inline bool laplace(const double *inputs, double *const outputs)
        {
            auto &x = inputs[0];
            auto &lap = outputs[0];
            lap = M_PI * M_PI * (cos(M_PI * x) * cos(M_PI * x) - sin(M_PI * x)) * exp(sin(M_PI * x));
            return true;
        }
    }
}

namespace DIM_NAMESPACE(Two)
{
    namespace Default
    {
        inline bool f(const double *inputs, double *const outputs)
        {
            auto &x = inputs[0];
            auto &y = inputs[1];
            auto &fxy = outputs[0];
            fxy = exp(y + sin(x));
            return true;
        }

        inline bool df(const double *inputs, double *const outputs)
        {
            auto &x = inputs[0];
            auto &y = inputs[1];
            auto &dfx = outputs[0];
            auto &dfy = outputs[1];
            dfx = cos(x) * exp(y + sin(x));
            dfy = exp(y + sin(x));
            return true;
        }

        inline bool laplace(const double *inputs, double *const outputs)
        {
            auto &x = inputs[0];
            auto &y = inputs[1];
            auto &lap = outputs[0];
            lap = -(-sin(x) + pow(cos(x), 2)) * exp(y + sin(x)) - exp(y + sin(x));
            return true;
        }
    }
}

#endif